package com.aldobucchi.graph.query
{
	
	import flash.events.Event;
	
	import mx.collections.ArrayCollection;
	import mx.collections.IList;
	
	import com.aldobucchi.core.dependency.DependencySpec;
	import com.aldobucchi.core.dependency.MXMLObjectWithDependenciesBase;
	import com.aldobucchi.utils.Arc;
	import com.aldobucchi.utils.IDisposable;
	import com.aldobucchi.utils.priority.DisplayListPriorityManager;



	/**
	 * 
	 * should be integrated to the rdf package.
	 * we need a metamodel of some kind ( metagraph? )
	 * 
	 * @author aldo.bucchi@gmail.com
	 * 
	 */
	public class NodeProp extends MXMLObjectWithDependenciesBase implements IDisposable
	{
		
		//////////////////////////////////////
		// node
		//////////////////////////////////////

		private var nodeSpec:DependencySpec = new DependencySpec( "node", "nodeChanged" );
		[Bindable("nodeChanged")]
		public function get node( ):*
		{
			return dh.getDependencyValue( nodeSpec );
		}
		public function set node( v:* ):void
		{
			dh.setDependencyValue( nodeSpec, v );
		}
		
		

		//////////////////////////////////////
		// arcLabel
		//////////////////////////////////////

		private var arcLabelSpec:DependencySpec = new DependencySpec( "arcLabel", "arcLabelChanged" );
		[Bindable("arcLabelChanged")]		
		public function get arcLabel( ):*
		{
			return dh.getDependencyValue( arcLabelSpec );
		}
		public function set arcLabel( v:* ):void
		{
			dh.setDependencyValue( arcLabelSpec, v );
		}		
		
		

		//////////////////////////////////////
		// arcInverted
		//////////////////////////////////////

		private var arcInvertedSpec:DependencySpec = new DependencySpec( "arcInverted", "arcInvertedChanged", null, false, false );
		[Bindable("arcInvertedChanged")]
		public function get arcInverted( ):Boolean
		{
			return dh.getDependencyValue( arcInvertedSpec );
		}
		public function set arcInverted( v:Boolean ):void
		{
			dh.setDependencyValue( arcInvertedSpec, v );
		}		



		//////////////////////////////////////
		// limit
		//////////////////////////////////////

		private var limitSpec:DependencySpec = new DependencySpec( "limit", "multipleChanged", null, false, 1 );
		[Bindable("limitChanged")]
		public function get limit( ):int
		{
			return dh.getDependencyValue( limitSpec );
		}
		public function set limit( v:int ):void
		{
			dh.setDependencyValue( limitSpec, v );
		}	



		//////////////////////////////////////
		// value
		//////////////////////////////////////

		private var _value:*;
		[Bindable("valueChanged")]
		public function get value( ):*
		{
			return _value;
		}



		//////////////////////////////////////
		// values
		//////////////////////////////////////

		private var _values:ArrayCollection = new ArrayCollection( );
		[Bindable("valuesChanged")] // never happens, just to keep binding warnings quiet
		public function get values( ):IList
		{
			return _values;
		}



		//////////////////////////////////////////////
		// executing
		//////////////////////////////////////////////		
		
		private var _executing:Boolean = false;
		[Bindable("executingChanged")]
		public function get executing( ):Boolean
		{
			return _executing;
		}




		
		
		private var query:Query = null;
		
		
		
		
		
		
		
		private function isReady( ):Boolean
		{
		
			if ( node == null || node == undefined )
				return false;
				
			if ( arcLabel == null || arcLabel == undefined )
				return false;
				
			return true;
		
		
		}
		
		
		
		
		
		
		override protected function dependenciesUpdated( ):void
		{

			if ( query != null )
			{
				query.cancel( );
				query.removeEventListener( QueryEvent.RESULT, query_result );
			}


			
			if ( ! isReady() ) // KLUDGE
				return;



			query = new Query( );
			query.addEventListener( QueryEvent.RESULT, query_result );
			query.addEventListener( QueryEvent.ERROR, query_error );
			
			if ( document )
				DisplayListPriorityManager.i.bindQueryToDisplayObject( query, document );
			else
				trace( "no document to bind query to" );
			
//			// create restriction tree nodes
//			
//			var rootVarNode		:RTVarNode 		= new RTVarNode( );
//			var rootFilterNode	:RTFilterNode 	= new RTFilterNode( new FilterEQ( node ) );
//			
//			var arcNode			:RTArcNode 		= new RTArcNode( Arc.instance( arcLabel, arcInverted ) );
//			var varNode			:RTVarNode 		= new RTVarNode( );
//			
//			// assemble restriction tree			
//			
//			rootVarNode.children.addItem( rootFilterNode );
//			rootVarNode.children.addItem( 	arcNode );
//											arcNode.children.addItem( varNode );
//			
//			
//			
//			
//			query.restrictionTreeRoot 	= rootVarNode;
//			query.selectVar 			= varNode;

			// dev time simplification
			query.simpleQueryNode 	= node;
			query.simpleQueryArc 	= Arc.instance( arcLabel, arcInverted );



			if ( limit != -1 )
				query.limit = limit;
			
			// TODO: offset, distinct, etc			
			
			setValue( undefined );
			
			query.execute( );
			
			setExecuting( true );
		
		}
		



		private function query_result( event:QueryEvent ):void
		{
		
			_values.disableAutoUpdate( );
			_values.removeAll( );

			try {

				for each ( var v:* in event.target.result )
				{
					_values.addItem( v );				
				}
			
			} catch ( e:Error ){ }			
			
			_values.enableAutoUpdate( );
			
			setValue( _values.length == 0 ? undefined : _values.getItemAt( 0 ) );
			
			setExecuting( false );
		
		}



		private function query_error( event:QueryEvent ):void
		{
			_values.removeAll( );
			setValue( undefined );
		}








		private function setValue( v:* ):void
		{
			if ( _value == v )
				return;
			_value = v;
			dispatchEvent( new Event( "valueChanged" ) );		
		}





		private function setExecuting( v:Boolean ):void
		{
			if ( _executing == v )
				return;
			_executing = v;
			dispatchEvent( new Event( "executingChanged" ) );
		}



		
		public function dispose( ):void
		{
			if ( query != null )
			{
				query.cancel( );
			}
		}



		
	}
	
}